/**
 * 
 */
package gov.nasa.anml.lifted;

import java.util.Arrays;

import gov.nasa.anml.PDDL;
import gov.nasa.anml.utility.*;

//import static gov.nasa.anml.lifted.SimpleBoolean.False;
//import static gov.nasa.anml.lifted.SimpleBoolean.True;
import static gov.nasa.anml.lifted.ANMLBoolean.*;
import static gov.nasa.anml.lifted.ANMLInteger.*;
import static gov.nasa.anml.lifted.ANMLFloat.*;

public enum Op {
	and(PDDL.Op.and) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return (a.v && b.v) ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v * b.v != 0 ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v * b.v != 0 ? True : False;
		}
	},
	or(PDDL.Op.or) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return (a.v || b.v) ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return (a.v != 0 || b.v != 0) ? True : False;
			//return a.v*a.v + b.v*b.v > 0 ? True : False;
			// Math.abs
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return (a.v != 0f || b.v != 0f) ? True : False;
		}
	},
	not(PDDL.Op.not) {
		public SimpleBoolean eval(SimpleBoolean a) {
			return !a.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a) {
			return a.v == 0 ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a) { 
			return a.v == 0f ? True : False;
		}
	},
	negate(null) {
		public SimpleBoolean eval(SimpleBoolean a) {
			return !a.v ? True : False;
		}
		public SimpleInteger eval(SimpleInteger a) {
			return ANMLInteger.make(-a.v);
		}
		public SimpleFloat eval(SimpleFloat a) { 
			return ANMLFloat.make(-a.v);
		}
	},
	equal(PDDL.Op.equals) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v == b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v == b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v == b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleString a,SimpleString b) {
			return SimpleString.equals(a,b) ? True : False;
		}
	},
	notEqual(null) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v != b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v != b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v != b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleString a,SimpleString b) {
			return !SimpleString.equals(a,b) ? True : False;
		}
	},
	lessThan(PDDL.Op.lt) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return !a.v && b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v < b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v < b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleString a,SimpleString b) {
			return SimpleString.compareTo(a,b) < 0 ? True : False;
		}
	},
	lessThanE(PDDL.Op.lte) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return !a.v || b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v <= b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v <= b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleString a,SimpleString b) {
			return SimpleString.compareTo(a,b) <= 0 ? True : False;
		}
	},
	greaterThan(PDDL.Op.gt) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v && !b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v > b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v > b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleString a,SimpleString b) {
			return SimpleString.compareTo(a,b) > 0 ? True : False;
		}
	},
	greaterThanE(PDDL.Op.gte) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v || !b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v >= b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v >= b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleString a,SimpleString b) {
			return SimpleString.compareTo(a,b) >= 0 ? True : False;
		}
	},
	implies(PDDL.Op.implies) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return !a.v || b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return a.v == 0 || b.v != 0 ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return a.v == 0 || b.v != 0 ? True : False;
		}
	},
	xor(null) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v ^ b.v ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a,SimpleInteger b) {
			return (a.v != 0) ^ (b.v != 0) ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a,SimpleFloat b) {
			return (a.v != 0f) ^ (b.v != 0f) ? True : False;
		}
	},
	add(PDDL.Op.plus) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v || b.v ? True : False;
		}
		public SimpleInteger eval(SimpleInteger a,SimpleInteger b) {
			return ANMLInteger.make(a.v + b.v);
		}
		public SimpleFloat eval(SimpleFloat a,SimpleFloat b) {
			return ANMLFloat.make(a.v + b.v);
		}
		public SimpleString eval(SimpleString a,SimpleString b) {
			int al = a.length, bl = b.length;
			char []d = new char[al + bl];
			System.arraycopy(a.v,0,d,0,al);
			System.arraycopy(b.v,0,d,al,bl);
			return ANMLString.make(d);
		}
	},
	subtract(PDDL.Op.minus) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v || !b.v ? True : False;
		}
		public SimpleInteger eval(SimpleInteger a,SimpleInteger b) {
			return ANMLInteger.make(a.v - b.v);
		}
		public SimpleFloat eval(SimpleFloat a,SimpleFloat b) {
			return ANMLFloat.make(a.v - b.v);
		}
	},
	multiply(PDDL.Op.times) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return a.v && b.v ? True : False;
		}
		public SimpleInteger eval(SimpleInteger a,SimpleInteger b) {
			return ANMLInteger.make(a.v * b.v);
		}
		public SimpleFloat eval(SimpleFloat a,SimpleFloat b) {
			return ANMLFloat.make(a.v * b.v);
		}
	},
	divide(PDDL.Op.divided_by) {
		public SimpleBoolean eval(SimpleBoolean a,SimpleBoolean b) {
			return b.v ? a : null;
		}
		public SimpleInteger eval(SimpleInteger a,SimpleInteger b) {
			return ANMLInteger.make(a.v / b.v);
		}
		public SimpleFloat eval(SimpleFloat a,SimpleFloat b) {
			return ANMLFloat.make(a.v / b.v);
		}
	},
	exists(null) {
		public SimpleBoolean eval(SimpleBoolean a) {
			return a != null ? True : False;
		}
		public SimpleBoolean eval(SimpleInteger a) {
			return a != null ? True : False;
		}
		public SimpleBoolean eval(SimpleFloat a) {
			return a != null ? True : False;
		}
		public SimpleBoolean eval(SimpleString a) {
			return a != null ? True : False;
		}		
	},
	booleanCast(null) {
		public SimpleBoolean eval(SimpleBoolean a) {
			return a;
		}
		public SimpleBoolean eval(SimpleInteger a) {
			return a.v == 0 ? False : True;
		}
		public SimpleBoolean eval(SimpleFloat a) {
			return a.v == 0f ? False : True;
		}
	},
	intCast(null) {
		public SimpleInteger eval(SimpleBoolean a) {
			return a.v ? One : Zero;
		}
		public SimpleInteger eval(SimpleInteger a) {
			return a;
		}
		public SimpleInteger eval(SimpleFloat a) {
			return ANMLInteger.make((int)a.v);
		}
	},
	floatCast(null) {
		public SimpleFloat eval(SimpleBoolean a) {
			return a.v ? OneF : ZeroF;
		}
		public SimpleFloat eval(SimpleInteger a) {
			return ANMLFloat.make(a.v);
		}
		public SimpleFloat eval(SimpleFloat a) {
			return a;
		}
	},
	ref(null) {
		public SimpleObject<?> eval(SimpleBoolean a) {return a;}
		public SimpleObject<?> eval(SimpleInteger a) {return a;}
		public SimpleObject<?> eval(SimpleFloat a) {return a;}
		public SimpleObject<?> eval(SimpleString a) {return a;}		
	},
	refNot(PDDL.Op.not) {
		public SimpleObject<?> eval(SimpleBoolean a) {return not.eval(a);}
		public SimpleObject<?> eval(SimpleInteger a) {return not.eval(a);}
		public SimpleObject<?> eval(SimpleFloat a) {return not.eval(a);}
		public SimpleObject<?> eval(SimpleString a) {return not.eval(a);}		
	},
	
	;
	// Literal instead of Value<?> in order to allow typeCode?
	// ditto in state?
	// need a better name than literal...
	//
	// in line with that -- BooleanLiteral?
	// instead of make() [asymmetry...]
	// course...asymmetry makes a bit of sense there, as bool is just two-valued...
	
	public SimpleObject<?> eval(SimpleBoolean a) {return null;}
	public SimpleObject<?> eval(SimpleInteger a) {return null;}
	public SimpleObject<?> eval(SimpleFloat a) {return null;}
	public SimpleObject<?> eval(SimpleString a) {return null;}		
	public SimpleObject<?> eval(SimpleBoolean a, SimpleBoolean b) {return null;}
	public SimpleObject<?> eval(SimpleInteger a, SimpleInteger b) { return null;}
	public SimpleObject<?> eval(SimpleFloat a, SimpleFloat b) { return null; }
	public SimpleObject<?> eval(SimpleString a, SimpleString b) { return null; }
	//public SimpleObject<?> eval(Object a, Object b) { return null; }
	
	public PDDL.Op pddl;
	Op(PDDL.Op pddl) {
		this.pddl = pddl;
	}
}

